/*
 * @autor Carlos Padoa
 */
package br.com.felix.fwt.servlet;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import br.com.felix.fwt.log.LoggerFactory;
import br.com.felix.fwt.security.exception.UnauthorizedAccessException;
import br.com.felix.fwt.servlet.exception.FormNotFoundException;
import br.com.felix.fwt.servlet.exception.UpdateFormFieldsException;
import br.com.felix.fwt.ui.Form;
import br.com.felix.fwt.ui.exception.ComponentRenderException;
import br.com.felix.fwt.ui.exception.UserInterfaceInitializationException;

/**
 * The Class FelixWebToolkitServlet is the Front Controller of the framework.
 * All of the website's requests are mapped to this Servlet. It works pretty
 * much like the Struts and JSF controllers. The workflow goes like this:
 * request => load form for the first time => store the form in memory =>
 * present to the client. request => load form from memory => update field
 * values => process form => present to the client.
 */
public class FelixStatelessFrontController implements FrontController {

	private Logger logger = LoggerFactory.getLogger(FelixStatelessFrontController.class);

	/**
	 * The Constant serialVersionUID required by the serializable interface.
	 */
	private static final long serialVersionUID = -576331252796570862L;



	/**
	 * @throws FormNotFoundException 
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void processRequestedForm(String requestedForm, FelixWebToolkitServlet felixWebToolkitServlet, HttpServletRequest request, HttpServletResponse resp) throws IOException, ServletException, FormNotFoundException {
		FwtContext threadLocalContext = felixWebToolkitServlet.getFwtContext();
		
		// multipart/form-data
		logger.debug("Content-type: " + request.getContentType());
		Form form = null;

		try {
			logger.info("Loading form '" + requestedForm + "'.");
			Class c = Class.forName(requestedForm);
			form = (Form) c.newInstance();
			threadLocalContext.form = form;
			try {
				setParameters(form, request);
				form.initializeForm();
			}
			catch (UserInterfaceInitializationException e) {
				logger.error("Error initializing form '" + requestedForm + "'. Executing treatment now.", e);
				form = form.catchUserInterfaceInitializationException(e);
				String formName = form.getName();
				logger.info("Executing form now is '" + formName + "'");
			}
			catch (UnauthorizedAccessException e) {
				logger.info("Unauthorized access to form '" + requestedForm + "'. Executing treatment now.", e);
				form = form.catchUnauthorizedAccessException(e);
				String formName = form.getName();
				logger.info("Executing form now is '" + formName + "'");
			}
			form.registerComponents();

			String action = null;
			action = form.updateFormFields(request);
			if (action != null) {
				logger.info("Requested action = '" + action + "'");
				if ("SVG".equals(action)) {
					PrintWriter outWriter = resp.getWriter();
					String svgComponent = request.getParameter("svg");
					logger.info("Printing SVG to output...");
					form.writeSVG(svgComponent, outWriter);
					outWriter.flush();
					return;
				}
				else if ("CHART".equals(action)) {
					resp.setContentType("image/png");
					OutputStream os = resp.getOutputStream();
					String chartComponent = request.getParameter("chart");
					logger.info("Printing CHART to output...");
					form.writeChart(chartComponent, os);
					os.flush();
					return;
				}
				else {
					form = form.executeAction(action);
					String formName = form.getName();
					logger.info("Executing form now is '" + formName + "'");
				}
			}
		}
		catch (ClassNotFoundException e) {
			String msg = "The following page was not found: '" + requestedForm + "'.";
			logger.fatal(msg, e);
			throw new FormNotFoundException(msg);
		}
		catch (InstantiationException e) {
			String msg = "The following page could not be loaded: '" + requestedForm + "'.";
			logger.fatal(msg, e);
			throw new FormNotFoundException(msg);
		}
		catch (IllegalAccessException e) {
			String msg = "The following page could not be loaded: '" + requestedForm + "'.";
			logger.fatal(msg, e);
			throw new FormNotFoundException(msg);
		}
		catch (UpdateFormFieldsException e) {
			logger.fatal("Error executing form '" + requestedForm + "'. Executing treatment now.", e);
			form = form.catchUpdateFormFieldsException(e);
			String formName = form.getName();
			logger.info("Executing form now is '" + formName + "'");
		}

		PrintWriter out = resp.getWriter();
		// resp.setCharacterEncoding("ISO-8859-1");
		resp.setContentType("text/html; charset=UTF-8");
		if (form != null) {
			try {
				form.write(out);
			}
			catch (ComponentRenderException e) {
				String msg = "Could NOT display form '" + requestedForm + "'. An error ocurred trying to render a component.";
				logger.error(msg, e);
				throw new ServletException(msg, e);
			}
			logger.info("Form '" + requestedForm + "' executed correctly");
		}
		else {
			logger.fatal("Form '" + requestedForm + "' executed INCORRECTLY");
		}
		out.flush();
	}



	private void setParameters(Form form, HttpServletRequest request) {
		String qs = request.getQueryString();
		if (qs!=null && qs.length()>0){
			Pattern p = Pattern.compile("&?[^=]*=");
			Matcher m = p.matcher(qs);
			String parameterName;
			int start = 0;
			int index;
			while (m.find(start)){
				parameterName = m.group();
				start = m.end();
				index = parameterName.indexOf('&');
				index = index >= 0? index + 1 : 0;
				parameterName = parameterName.substring(index, parameterName.length()-1);
				form.setParameter(parameterName, request.getParameter(parameterName));
			}
		}
	}

}
